Node.js - tutorial - npm package manager

revision:


npm: standard package manager for Node.js

In January 2017 over 350,000 packages were reported being listed in the npm registry, making it the biggest single language code repository on Earth, and you can be sure there is a package for (almost!) everything. It started as a way to download and manage dependencies of Node.js packages, but it has since become a tool used also in frontend JavaScript.

There are many things that npm does.

npm manages downloads of dependencies of your project.

If a project has a package.json file, by running "npm install" it will install everything the project needs, in the node_modules folder, creating it if it's not existing already.

You can also install a specific package by running "npm install <package-name>. Furthermore, since npm 5, this command adds <package-name> to the package.json file dependencies. Before version 5, you needed to add the flag "--save".

Often you'll see more flags added to this command:
--save-dev installs and adds the entry to the package.json file devDependencies;
--no-save installs but does not add the entry to the package.json file dependencies;
--save-optional installs and adds the entry to the package.json file optionalDependencies;
--no-optional will prevent optional dependencies from being installed.

Shorthands of the flags can also be used:
-S: --save;
-D: --save-dev;
-O: --save-optional.

The difference between devDependencies and dependencies is that the former contains development tools, like a testing library, while the latter is bundled with the app in production.

As for the optionalDependencies the difference is that build failure of the dependency will not cause installation to fail. But it is your program's responsibility to handle the lack of the dependency.

Updating is also made easy, by running "npm update". npm will check all packages for a newer version that satisfies your versioning constraints.You can specify a single package to update as well: "npm update <package-name>.

npm also manages versioning, so you can specify any specific version of a package, or require a version higher or lower than what you need. Many times you'll find that a library is only compatible with a major release of another library. Or a bug in the latest release of a lib, still unfixed, is causing an issue. Specifying an explicit version of a library also helps to keep everyone on the same exact version of a package, so that the whole team runs the same version until the package.json file is updated. In all those cases, versioning helps a lot, and npm follows the semantic versioning (semver) standard.

The package.json file supports a format for specifying command line tasks that can be run by using "npm run <task-name>.

example

CONSOLE:
      $ npm run watch
      $ npm run dev
      $ npm run prod
    

where does npm install the packages?

When you install a package using npm you can perform 2 types of installation: a local install, a global install

By default, when you type an npm install command, like "npm install lodash", the package is installed in the current file tree, under the node_modules subfolder. As this happens, npm also adds the lodash entry in the dependencies property of the package.json file present in the current folder.

A global installation is performed using the -g flag: "npm install -g lodash". When this happens, npm won't install the package under the local folder, but instead, it will use a global location.The npm root -g command will tell you where that exact location is on your machine.

On macOS or Linux this location could be /usr/local/lib/node_modules.
On Windows it could be C:\Users\YOU\AppData\Roaming\npm\node_modules

If you use nvm to manage Node.js versions, however, that location would differ.


how to use or execute a package installed using npm?

npm install lodash is going to install the package in the local node_modules folder.

To use it in your code, you just need to import it into your program using require: const _ = require('lodash')

if your package is an executable, it will put the executable file under the node_modules/.bin/ folder.


the package.json guide

The package.json file is kind of a manifest for your project. It can do a lot of things, completely unrelated. It's a central repository of configuration for tools, for example. It's also where npm and yarn store the names and versions for all the installed packages.

example of package.json file

{}

It's empty! There are no fixed requirements of what should be in a package.json file, for an application. The only requirement is that it respects the JSON format, otherwise it cannot be read by programs that try to access its properties programmatically.

If you're building a Node.js package that you want to distribute over npm things change radically, and you must have a set of properties that will help other people use it.

example of other package.json file.

 JSON
      {
        "name": "test-project"
      }
    

The properties you can use in detail are as follows:

name: sets the package name. The name must be less than 214 characters, must not have spaces, it can only contain lowercase letters, hyphens (-) or underscores (_). This is because when a package is published on npm, it gets its own URL based on this property.

example

JSON:
      "name": "test-project"
    

author: lists the package author name.

example

JSON:
      {
        "author": "Joe <joe@whatever.com&ht; (https://whatever.com)"
      }

      or

      {
        "author": {
          "name": "Joe",
          "email": "joe@whatever.com",
          "url": "https://whatever.com"
        }
      }
    

contributors: the project can have one or more contributors. This property is an array that lists them.

bugs: links to the package issue tracker, most likely a GitHub issues page

homepage: sets the package homepage.

version: indicates the current version of the package. This property follows the semantic versioning (semver) notation for versions, which means the version is always expressed with 3 numbers: x.x.x. The first number is the major version, the second the minor version and the third is the patch version. There is a meaning in these numbers: a release that only fixes bugs is a patch release, a release that introduces backward-compatible changes is a minor release, a major release can have breaking changes.

license: indicates the license of the package.

keywords: this property contains an array of keywords that associate with what your package does. This helps people find your package when navigating similar packages.

description: this property contains a brief description of the package. This is especially useful if you decide to publish your package to npm so that people can find out what the package is about.

repository: this property specifies where this package repository is located.

main: sets the entry point for the package. When you import this package in an application, that's where the application will search for the module exports.

private: if set to true prevents the app/package to be accidentally published on npm.

scripts: defines a set of node scripts you can run. These scripts are command line applications. You can run them by calling npm run XXXX or yarn XXXX, where XXXX is the command name. Example: npm run dev. You can use any name you want for a command, and scripts can do literally anything you want.

dependencies: sets a list of npm packages installed as dependencies. When you install a package using npm or yarn that package is automatically inserted in this list.

devDependencies: sets a list of npm packages installed as development dependencies. They differ from dependencies because they are meant to be installed only on a development machine, not needed to run the code in production. When you install a package using npm or yarn that package is automatically inserted in this list.

engines: sets which versions of Node.js and other commands this package/app work on.

browserslist: is used to tell which browsers (and their versions) you want to support. It's referenced by Babel, Autoprefixer, and other tools, to only add the polyfills and fallbacks needed to the browsers you target.

Command-specific properties: the package.json file can also host command-specific configuration, for example for Babel, ESLint, and more. Each has a specific property, like eslintConfig, babel and others. Those are command-specific, and you can find how to use those in the respective command/project documentation.


the package-lock.json file

In version 5, npm introduced the package-lock.json file. The goal of package-lock.json file is to keep track of the exact version of every package that is installed so that a product is 100% reproducible in the same way even if packages are updated by their maintainers.

This solves a very specific problem that package.json left unsolved. In package.json you can set which versions you want to upgrade to (patch or minor), using the semver notation

The package-lock.json sets your currently installed version of each package in stone, and npm will use those exact versions when running npm install. The package-lock.json file needs to be committed to your Git repository, so it can be fetched by other people, if the project is public or you have collaborators, or if you use Git as a source for deployments. The dependencies versions will be updated in the package-lock.json file when you run npm update.


find the installed version of an npm package

To see the version of all installed npm packages, including their dependencies:" npm list".You can also just open the package-lock.json file, but this involves some visual scanning.

npm list -g is the same, but for globally installed packages.To get only your top-level packages (basically, the ones you told npm to install and you listed in the package.json), run "npm list --depth=0".

You can get the version of a specific package by specifying its name. This also works for dependencies of packages you installed.

If you want to see what's the latest available version of the package on the npm repository, run "npm view [package_name] version".

P.S. You can install an old version of an npm package using the @ syntax: e.g. npm install <package>@<version>. The same can be done with global packages: e.g. npm install -g webpack@4.16.4.

P.S. You might also be interested in listing all the previous versions of a package. You can do it with "npm view <package> versions".


update all the Node.js dependencies to their latest version.

When you install a package using npm install <packagename>, the latest available version of the package is downloaded and put in the node_modules folder, and a corresponding entry is added to the package.json and package-lock.json files that are present in your current folder. npm calculates the dependencies and installs the latest available version of those as well.

If there is a new minor or patch release and we type npm update, the installed version is updated, and the package-lock.json file diligently filled with the new version. Since npm version 5.0.0, npm update will update the package.json with the updated version. Use npm update --no-save to not update package.json.

To discover new releases of the packages, you run npm outdated.

To update all packages to a new major version, install the npm-check-updates package globally "npm install -g npm-check-updates" then run it "ncu -u". This will upgrade all the version hints in the package.json file, to dependencies and devDependencies, so npm can install the new major version.

You are now ready to run the update:npm update


uninstalling npm packages

To uninstall a package you have previously installed locally (using npm install <package-name> in the node_modules folder, run "npm uninstall <package-name> from the project root folder (the folder that contains the node_modules folder).

Using the -S flag, or --save, this operation will also remove the reference in the package.json file. package.json will be automatically updated with devDependency and dependency once you uninstall npm package.

If the package is installed globally, you need to add the -g / --global flag:" npm uninstall -g <package-name>". yourou can run this command from anywhere you want on your system because the folder where you currently are does not matter.


npm global or local packages

The main difference between local and global packages is this:
- local packages are installed in the directory where you run npm install <package-name>, and they are put in the node_modules folder under this directory;
- global packages are all put in a single place in your system (exactly where depends on your setup), regardless of where you run npm install -g <package-name>

In general, all packages should be installed locally. This makes sure you can have dozens of applications in your computer, all running a different version of each package if needed.

Updating a global package would make all your projects use the new release, and as you can imagine this might cause nightmares in terms of maintenance, as some packages might break compatibility with further dependencies, and so on.

All projects have their own local version of a package, even if this might appear like a waste of resources, it's minimal compared to the possible negative consequences

A package should be installed globally when it provides an executable command that you run from the shell (CLI), and it's reused across projects. You can also install executable commands locally and run them using npx, but some packages are just better installed globally.